﻿using System;
using System.Collections.Generic;

namespace ZeBobo5.LibraryHelper.Orm
{
    public abstract class OrmDataElementBase<A> : IOrmDataElementBase
        where A : IOrmDataElementBase
    {
        #region Delegates

        public delegate void FieldChangedEventHandler(OrmDataElementBase<A> sender, OrmFieldBase<A> field);

        public delegate void FieldChangingEventHandler(OrmDataElementBase<A> sender, OrmFieldBase<A> field);

        #endregion

        internal readonly Dictionary<OrmFieldBase<A>, Type> _colFields = new Dictionary<OrmFieldBase<A>, Type>();
        private OrmDataElementState _ormState = OrmDataElementState.None;
        internal string _strConnectionName;

        public OrmDataElementState State
        {
            get { return _ormState; }
            internal set { _ormState = value; }
        }

        internal void AddOwnerField<T>(OrmFieldBase<A> field)
        {
            if (!_colFields.ContainsKey(field))
            {
                _colFields.Add(field, typeof (T));
            }
        }

        internal void SetCurrentValuesToOriginals()
        {
            foreach (KeyValuePair<OrmFieldBase<A>, Type> pair in _colFields)
            {
                pair.Key.GetValues(this).SetCurrentToOriginal();
            }
        }

        public OrmDataElementList<B> LinkTo<B>()
            where B : OrmDataElementBase<B>, new()
        {
            OrmDataFactory<B> factory = OrmDataFactory<B>.
                Select(_strConnectionName);

            List<OrmDataLink<A, B>> lstLinkWhere = OrmDatabaseSchema.GetLinks<A, B>();

            if (lstLinkWhere.Count > 0)
            {
                foreach (OrmDataLink<A, B> link in lstLinkWhere)
                {
                    if (link.FieldA.ValuesType().Name.ToLower() == "string")
                    {
                        factory.Where(OrmWhereFactory.Comparators<B>.EqualityString(link.FieldB, (string) link.FieldA.GetValues(this)._currentValue));
                    }
                    else
                    {
                        factory.Where(OrmWhereFactory.Comparators<B>.Equality(link.FieldB, link.FieldA.GetValues(this)._currentValue));
                    }
                }
                return factory.Execute();
            }
            throw new Exception("No link found.");
        }

        public OrmDataElementList<B> LinkTo<B>(OrmWhereFactory where)
            where B : OrmDataElementBase<B>, new()
        {
            OrmDataFactory<B> factory = OrmDataFactory<B>.
                Select(_strConnectionName);

            List<OrmDataLink<A, B>> lstLinkWhere = OrmDatabaseSchema.GetLinks<A, B>();

            if (lstLinkWhere.Count > 0)
            {
                foreach (OrmDataLink<A, B> link in lstLinkWhere)
                {
                    if (link.FieldA.ValuesType().Name.ToLower() == "string")
                    {
                        factory.Where(OrmWhereFactory.Comparators<B>.EqualityString(link.FieldB, (string) link.FieldA.GetValues(this)._currentValue));
                    }
                    else
                    {
                        factory.Where(OrmWhereFactory.Comparators<B>.Equality(link.FieldB, link.FieldA.GetValues(this)._currentValue));
                    }
                }
                factory.Where(where);
                return factory.Execute();
            }
            throw new Exception("No link found.");
        }

        public OrmDataElementList<B> LinkTo<B>(OrmOrderByFactory<B> orderBy)
            where B : OrmDataElementBase<B>, new()
        {
            OrmDataFactory<B> factory = OrmDataFactory<B>.
                Select(_strConnectionName);

            List<OrmDataLink<A, B>> lstLinkWhere = OrmDatabaseSchema.GetLinks<A, B>();

            if (lstLinkWhere.Count > 0)
            {
                foreach (OrmDataLink<A, B> link in lstLinkWhere)
                {
                    if (link.FieldA.ValuesType().Name.ToLower() == "string")
                    {
                        factory.Where(OrmWhereFactory.Comparators<B>.EqualityString(link.FieldB, (string) link.FieldA.GetValues(this)._currentValue));
                    }
                    else
                    {
                        factory.Where(OrmWhereFactory.Comparators<B>.Equality(link.FieldB, link.FieldA.GetValues(this)._currentValue));
                    }
                }
                factory.OrderBy(orderBy);
                return factory.Execute();
            }
            throw new Exception("No link found.");
        }

        public OrmDataElementList<B> LinkTo<B>(OrmWhereFactory where, OrmOrderByFactory<B> orderBy)
            where B : OrmDataElementBase<B>, new()
        {
            OrmDataFactory<B> factory = OrmDataFactory<B>.
                Select(_strConnectionName);

            List<OrmDataLink<A, B>> lstLinkWhere = OrmDatabaseSchema.GetLinks<A, B>();

            if (lstLinkWhere.Count > 0)
            {
                foreach (OrmDataLink<A, B> link in lstLinkWhere)
                {
                    if (link.FieldA.ValuesType().Name.ToLower() == "string")
                    {
                        factory.Where(OrmWhereFactory.Comparators<B>.EqualityString(link.FieldB, (string) link.FieldA.GetValues(this)._currentValue));
                    }
                    else
                    {
                        factory.Where(OrmWhereFactory.Comparators<B>.Equality(link.FieldB, link.FieldA.GetValues(this)._currentValue));
                    }
                }
                factory.Where(where);
                factory.OrderBy(orderBy);
                return factory.Execute();
            }
            throw new Exception("No link found.");
        }

        public event FieldChangingEventHandler FieldChanging;
        public event FieldChangedEventHandler FieldChanged;

        public void OnFieldChanged(OrmFieldBase<A> sender)
        {
            if (State != OrmDataElementState.ToInsert && State != OrmDataElementState.Deleted)
            {
                State = OrmDataElementState.ToUpdate;
            }
            if (FieldChanged != null)
            {
                FieldChanged(this, sender);
            }
        }

        public void OnFieldChanging(OrmFieldBase<A> sender)
        {
            if (FieldChanging != null)
            {
                FieldChanging(this, sender);
            }
        }

        private OrmFieldBase<A> _defaultFieldToString;

        public OrmFieldBase<A> DefaultFieldToString
        {
            set
            {
                _defaultFieldToString = value;
            }
        }

        public override string ToString()
        {
            if(_defaultFieldToString == null)
                return GetType().Name;
            return _defaultFieldToString.GetValues(this)._currentValue.ToString();
        }
    }
}